package com.pig.easy.bpm.interceptor;

import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.pig.easy.bpm.constant.BpmConstant;
import com.pig.easy.bpm.entityError.EntityError;
import com.pig.easy.bpm.execption.BpmException;
import com.pig.easy.bpm.utils.JsonResult;
import com.pig.easy.bpm.utils.SnowKeyGenUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.rpc.RpcContext;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.MDC;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.UndeclaredThrowableException;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;

/**
 * todo: controller 拦截器,记录 请求日志 方便排查问题
 */
@Slf4j
@Component
@Aspect
@Order(100)
public class ControllerLogInterceptor {


    /**
     * 定义拦截规则：拦截controller包下面的所有类中
     */
    @Pointcut("execution(public * com.pig.easy.bpm.controller..*.*(..))")
    public void controllerMethodPointcut() {
    }

    @Before("controllerMethodPointcut()")
    public void doBefore(JoinPoint joinPoint) throws Throwable {
        String traceId = SnowKeyGenUtils.getInstance().getNextId();
        MDC.put(BpmConstant.TRACE_ID, traceId);
        RpcContext.getContext().setAttachment(BpmConstant.TRACE_ID,traceId);
    }

    @Before("controllerMethodPointcut()")
    public void doAfter() throws Throwable {
        MDC.clear();
    }
    /**
     * 功能描述: 拦截器具体实现
     */
    @Around("controllerMethodPointcut()")
    public Object Interceptor(ProceedingJoinPoint pjp) {

        long beginTime = System.currentTimeMillis();
        MethodSignature signature = (MethodSignature) pjp.getSignature();
        Method method = signature.getMethod();
        String methodName = method.getName();

        Set<Object> allParams = new LinkedHashSet<>();
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        String ip = request.getRemoteAddr();
        Object result = null;

        Object[] args = pjp.getArgs();
        for (Object arg : args) {
            if (arg instanceof Map<?, ?>) {
                @SuppressWarnings("unchecked")
                Map<String, Object> map = (Map<String, Object>) arg;
                allParams.add(map);
            } else if (arg instanceof HttpServletRequest) {
                HttpServletRequest httpServletRequest = (HttpServletRequest) arg;
                Map<String, String[]> paramMap = httpServletRequest.getParameterMap();
                if (paramMap != null && paramMap.size() > 0) {
                    allParams.add(paramMap);
                }
            } else {
                allParams.add(arg);
            }
        }
        String requestId = SnowKeyGenUtils.getInstance().getNextId();
        log.info("request[{}]start，IP：{}，method：{}，param：{}", requestId, ip, methodName, allParams);
        try {
            // 一切正常的情况下，继续执行被拦截的方法
            result = pjp.proceed();
        } catch (Throwable e) {
            log.error("request[{}]param：{}，execption：{} ", requestId, allParams, e);
            if(e instanceof UndeclaredThrowableException){
                Throwable targetException = ((InvocationTargetException) ((UndeclaredThrowableException) e).getUndeclaredThrowable()).getTargetException();
                if (targetException instanceof BpmException) {
                    result  = JsonResult.error(((BpmException) targetException).getEntityError());
                } else {
                    result  = JsonResult.error(EntityError.SYSTEM_ERROR);
                }
            } else {
                result  = JsonResult.error(EntityError.SYSTEM_ERROR);
            }
        }
        long costMs = System.currentTimeMillis() - beginTime;
        try {
            log.info("request[{}]end，IP：{}，method：{}，spendTime：{}ms，result：{}", requestId, ip, methodName, costMs, JSONObject.toJSONStringWithDateFormat(result, "yyyy-MM-dd HH:mm:ss", SerializerFeature.WriteDateUseDateFormat));
        } catch (Exception e) {
            log.info("request[{}]end，IP：{}，method：{}，spendTime：{}ms，result：{}", requestId, ip, methodName, costMs, result);
        }
        return result;
    }
}
